using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Admin.NET.Application.Entity.Business;
using Admin.NET.Application.EventBus;
using Admin.NET.Application.Service.Bill.Dto;
using Admin.NET.Core;
using Admin.NET.Core.Service;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Furion.Schedule;
using Furion.TimeCrontab;
using Lazy.Captcha.Core;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SqlSugar;

namespace Admin.NET.Application.Service.Bill;

/// <summary>
/// 账单服务
/// </summary>
[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class BillService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly SqlSugarRepository<BillType> _rep;

	private readonly IEventPublisher _eventPublisher;

	private readonly UserManager _userManager;

	private readonly ISchedulerFactory _schedulerFactory;

	private readonly SysConfigService _sysConfigService;

	private readonly ICaptcha _captcha;

	public BillService(SqlSugarRepository<BillType> rep, IEventPublisher eventPublisher, UserManager userManager, ISchedulerFactory schedulerFactory, SysConfigService sysConfigService, ICaptcha captcha)
	{
		_rep = rep;
		_eventPublisher = eventPublisher;
		_userManager = userManager;
		_schedulerFactory = schedulerFactory;
		_sysConfigService = sysConfigService;
		_captcha = captcha;
	}

	[HttpGet("/bill/billtypes/page")]
	public async Task<dynamic> GetBillTypesPage([FromQuery] BillTypeInput input)
	{
		ISugarQueryable<BillType> query = _rep.Context.Queryable<BillType>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (BillType u) => u.Name.Contains(input.Name.Trim())).WhereIF(input.IsSupportedInvoicing.HasValue, (BillType u) => (int?)u.IsSupportedInvoicing == input.IsSupportedInvoicing);
		query = query.OrderBuilder(input);
		return await query.ToPagedListAsync(input.Page, input.PageSize);
	}

	/// <summary>
	///
	/// 获取账单类型列表
	/// </summary>
	/// <returns></returns>
	[HttpGet("/bill/billtypes")]
	public async Task<dynamic> GetBillTypes()
	{
		return await _rep.AsQueryable().ToListAsync();
	}

	/// <summary>
	/// 添加账单类型
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/addbilltype")]
	public async Task AddBillType(AddBillTypeInput input)
	{
		if (await (from w in _rep.AsQueryable()
			where w.Name == input.Name.Trim()
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("费用类别已存在");
		}
		BillType data = input.Adapt<BillType>();
		await _rep.InsertAsync(data);
	}

	/// <summary>
	/// 删除账单类型
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/deletebilltype")]
	public async Task DeleteBillType(DeleteBillTypeInput input)
	{
		if (await (from w in _rep.Context.Queryable<BillTemplate>()
			where SqlFunc.JsonArrayAny(w.BillTypes, input.Id)
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("该费用类别已被使用，无法删除");
		}
		if (await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where SqlFunc.JsonListObjectAny(w.Bills, "BillType", input.Id)
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("该费用类别已被使用，无法删除");
		}
		BillType entity = await _rep.GetFirstAsync((BillType u) => u.Id == input.Id);
		await _rep.DeleteAsync(entity);
	}

	/// <summary>
	/// 帐单模板列表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/bill/gettemplates")]
	public async Task<dynamic> GetTemplates([FromQuery] BillTemplateInput input)
	{
		ISugarQueryable<BillTemplate> query = _rep.Context.Queryable<BillTemplate>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (BillTemplate u) => u.Name.Contains(input.Name.Trim()));
		query = query.OrderBuilder(input);
		return await query.Select((BillTemplate u) => new
		{
			BillTypes = u.BillTypes,
			CallRules = u.CallRules,
			CreateTime = u.CreateTime,
			CreateUserId = u.CreateUserId,
			CreateUserName = u.CreateUserName,
			ExpiryDate = u.ExpiryDate,
			Id = u.Id,
			IsDelete = u.IsDelete,
			LateFee = u.LateFee,
			LimitAmount = u.LimitAmount,
			Name = u.Name,
			TenantId = u.TenantId,
			UpdateTime = u.UpdateTime,
			UpdateUserId = u.UpdateUserId,
			UpdateUserName = u.UpdateUserName,
			Status = u.Status,
			ExecuteTime = u.ExecuteTime,
			TotalCount = (from c in SqlFunc.Subqueryable<CustomRoomBill>()
				where c.BillTemplateId == u.Id
				select c).Count(),
			CloseCount = (from c2 in SqlFunc.Subqueryable<CustomRoomBill>()
				where c2.BillTemplateId == u.Id && c2.Status == 2
				select c2).Count(),
			NotPushStatus = (from c3 in SqlFunc.Subqueryable<CustomRoomBill>()
				where c3.BillTemplateId == u.Id && c3.Status == 0
				select c3).Count()
		}).ToPagedListAsync(input.Page, input.PageSize);
	}

	/// <summary>
	/// 创建账单模板
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/addbilltemplate")]
	public async Task AddTemplate(AddBillTemplateInput input)
	{
		if (await (from w in _rep.Context.Queryable<BillTemplate>()
			where w.Name.Equals(input.Name.Trim())
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("模板名称已存在，请更改后重试");
		}
		BillTemplate entity = input.Adapt<BillTemplate>();
		await _rep.Context.Insertable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 删除模板
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/deletetemplate")]
	public async Task DeleteTemplate(DeleteBillTemplateInput input)
	{
		BillTemplate entity = await (from u in _rep.Context.Queryable<BillTemplate>()
			where u.Id == input.Id
			select u).FirstAsync();
		if (entity == null)
		{
			throw Oops.Bah("模板不存在或已被删除，请刷新后重试");
		}
		if (await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where w.BillTemplateId == input.Id
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("账单已存在于房间，无法删除");
		}
		await _rep.Context.Deleteable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 编辑模板
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/edittemplate")]
	public async Task EditTemplate(UpdateBillTemplateInput input)
	{
		if (await (from w in _rep.Context.Queryable<BillTemplate>()
			where w.Id != input.Id && w.Name.Equals(input.Name.Trim())
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("模板名称已存在，请更改后重试");
		}
		BillTemplate entity = input.Adapt<BillTemplate>();
		ChangeJob(entity.Id, entity);
		await _rep.Context.Updateable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 保存房间账单(此方法无效)
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/saveroombills")]
	public async Task SaveRoomBills(RoomBillsInput input)
	{
		BillTemplate template = await (from w in _rep.Context.Queryable<BillTemplate>()
			where w.Id == input.TemplateId
			select w).FirstAsync();
		if (template == null)
		{
			throw Oops.Bah("无效账单，账单模板不存在或已被删除");
		}
		List<long> allbilltype = (from s in input.Bills.SelectMany((RoomBill s) => s.Bills)
			select s.BillType).ToList();
		List<long> haserr = allbilltype.Except(template.BillTypes).ToList();
		if (haserr.Count > 0)
		{
			throw Oops.Bah("无效账单，账单与模板不匹配");
		}
		List<CustomRoomBill> allbills = await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where w.BillTemplateId == input.TemplateId
			select w).ToListAsync();
		List<long?> ids = (from w in input.Bills
			where w.Id.HasValue
			select w into s
			select s.Id).ToList();
		allbills.Where((CustomRoomBill w) => !ids.Contains(w.Id) && w.Status != 2).ToList();
		List<CustomRoomBill> entity = input.Bills.Adapt<List<CustomRoomBill>>();
		await _rep.Context.Storageable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 保存房间账单，单个房间
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/bill/saveroombill")]
	public async Task<long> SaveRoomBill(CustomRoomBillInput input)
	{
		BillTemplate template = await (from w in _rep.Context.Queryable<BillTemplate>()
			where w.Id == input.BillTemplateId
			select w).FirstAsync();
		if (template == null)
		{
			throw Oops.Bah("无效账单，账单模板不存在或已被删除");
		}
		List<long> allbilltype = input.Bills.Select((CustomBill s) => s.BillType).ToList();
		List<long> haserr = allbilltype.Except(template.BillTypes).ToList();
		if (haserr.Count > 0)
		{
			throw Oops.Bah("无效账单，账单与模板不匹配");
		}
		long? outputId = input.Id;
		if (!input.Id.HasValue)
		{
			if (await (from w in _rep.Context.Queryable<CustomRoomBill>()
				where w.BillTemplateId == input.BillTemplateId && w.RoomId == input.RoomId
				select w).CountAsync() > 0)
			{
				throw Oops.Bah("该房间账单已存在，无需重复创建");
			}
			CustomRoomBill data = input.Adapt<CustomRoomBill>();
			CustomRoomBill customRoomBill = data;
			customRoomBill.OwnerId = await (from w in _rep.Context.Queryable<Room>()
				where w.Id == input.RoomId
				select w into s
				select s.OwnerId).FirstAsync();
			await _rep.Context.Insertable(data).ExecuteCommandAsync();
			outputId = data.Id;
		}
		else
		{
			CustomRoomBill data = await (from w in _rep.Context.Queryable<CustomRoomBill>()
				where (long?)w.Id == input.Id
				select w).FirstAsync();
			if (data == null)
			{
				throw Oops.Bah("保存失败，请刷新后重试");
			}
			if (data.Status == 2)
			{
				throw Oops.Bah("该账单已关闭，无法保存");
			}
			if (data.RoomId != input.RoomId || data.BillTemplateId != input.BillTemplateId)
			{
				throw Oops.Bah("保存失败，请刷新后重试");
			}
			data.Bills = input.Bills;
			data.Remark = input.Remark;
			await _rep.Context.Updateable(data).UpdateColumns((CustomRoomBill s) => new { s.Bills, s.Remark }).ExecuteCommandAsync();
		}
		return outputId.Value;
	}

	/// <summary>
	/// 获取账单下的房间明细
	/// </summary>
	/// <param name="id"></param>
	/// <returns></returns>
	[HttpGet("/bill/getroombills")]
	public async Task<dynamic> GetRoomBills([FromQuery] long id)
	{
		return await (from s in _rep.Context.Queryable<CustomRoomBill>()
			where s.BillTemplateId == id
			select new
			{
				Id = s.Id,
				RoomId = s.RoomId,
				Bills = s.Bills,
				Status = s.Status,
				Remark = s.Remark,
				RoomName = from w1 in SqlFunc.Subqueryable<Room>()
					where w1.Id == s.RoomId
					select w1.Name,
				RoomArea = from w1 in SqlFunc.Subqueryable<Room>()
					where w1.Id == s.RoomId
					select w1.Area
			}).ToListAsync();
	}

	/// <summary>
	/// 下发账单
	/// </summary>
	/// <param name="templateId"></param>
	/// <returns></returns>
	[HttpPost("/bill/sendroombills")]
	public async Task SendRoomBills([FromBody] SendRoomBillInput input, [FromQuery] string code, [FromQuery] string codeId)
	{
		try
		{
			if (await _sysConfigService.GetConfigValue<bool>("sys_captcha"))
			{
				if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(codeId))
				{
					throw Oops.Bah("请先输入图形验证码");
				}
				if (!_captcha.Validate(codeId, code))
				{
					throw Oops.Bah("图形验证码无效或错误");
				}
			}
			await _rep.Context.Ado.BeginTranAsync();
			long templateId = input.TemplateId;
			BillTemplate template = await (from w in _rep.Context.Queryable<BillTemplate>()
				where w.Id == templateId
				select w).FirstAsync();
			if (template == null)
			{
				throw Oops.Bah("请先设置催缴方案");
			}
			List<long> roomIds = await (from w in _rep.Context.Queryable<CustomRoomBill>()
				where w.BillTemplateId == templateId && w.Status != 2
				select w into s
				select s.RoomId).ToListAsync();
			if (roomIds.Count == 0)
			{
				throw Oops.Bah("请至少添加一个房间账单");
			}
			List<long?> ownerIds = await (from w in _rep.Context.Queryable<Room>()
				where roomIds.Contains(w.Id) && w.OwnerId != null
				select w into s
				select s.OwnerId).ToListAsync();
			if (ownerIds.Count == 0)
			{
				throw Oops.Bah("房间未绑定业主");
			}
			await (from s in _rep.Context.Updateable<CustomRoomBill>().SetColumns((CustomRoomBill s) => s.Status == 1)
				where s.BillTemplateId == templateId && s.Status == 0
				select s).ExecuteCommandAsync();
			List<Owner> owners = await (from w in _rep.Context.Queryable<Owner>()
				where ownerIds.Contains(w.Id)
				select w into s
				select new Owner
				{
					Email = s.Email,
					Id = s.Id,
					AppId = s.AppId,
					OpenId = s.OpenId,
					PhoneNumber = s.PhoneNumber,
					Name = s.Name
				}).ToListAsync();
			if (owners.Count > 0)
			{
				string enddate = template.ExpiryDate.ToString("yyyy-MM-dd");
				BillNoticePayload billNoticePayload = new BillNoticePayload
				{
					BillTemplateId = templateId,
					CallRules = template.CallRules,
					EndDate = enddate,
					Body = "您有新的账单待支付，请于" + enddate + "前确认支付",
					Owners = owners,
					Subject = (template.Name ?? ""),
					Remark = "若已支付，请忽略本消息",
					Path = "/userPage/otherbill/otherbill"
				};
				EventPayload payload = new EventPayload
				{
					Data = JsonConvert.SerializeObject(billNoticePayload),
					TenantId = _userManager.TenantId,
					UserId = _userManager.UserId
				};
				for (int i = 0; i < 4; i++)
				{
					_eventPublisher.PublishAsync($"{i}:SendBillNotice", payload).Wait();
				}
			}
			ChangeJob(templateId, template);
			await _rep.Context.Updateable(template).ExecuteCommandAsync();
			await _rep.Context.Ado.CommitTranAsync();
		}
		catch (Exception ex)
		{
			await _rep.Context.Ado.RollbackTranAsync();
			throw ex;
		}
	}

	/// <summary>
	/// 修改任务
	/// </summary>
	/// <param name="templateId"></param>
	/// <param name="template"></param>
	private void ChangeJob(long templateId, BillTemplate template)
	{
		Dictionary<string, object> dic = new Dictionary<string, object>
		{
			{
				"templateId",
				templateId.ToString()
			},
			{
				"tenantId",
				_userManager.TenantId.ToString()
			}
		};
		JobBuilder job = JobBuilder.Create<CustomRoomBillJob>().SetJobId(templateId.ToString()).SetJobType<CustomRoomBillJob>()
			.SetConcurrent(concurrent: false)
			.SetGroupName("default")
			.SetIncludeAnnotations(includeAnnotations: false)
			.SetProperties(dic)
			.SetDescription("催缴方案:" + template.Name);
		DateTime endDate = template.ExpiryDate.AddDays(1.0);
		int year = endDate.Year;
		int month = endDate.Month;
		int day = endDate.Day;
		TriggerBuilder trigger = TriggerBuilder.Create<CronTrigger>(new object[2]
		{
			$"0 10 {day} {month} ? {year}",
			CronStringFormat.WithYears
		}).SetTriggerId(templateId.ToString()).SetStartNow(startNow: true)
			.SetRunOnStart(runOnStart: false);
		template.ExecuteTime = endDate.AddHours(10.0);
		template.Status = 0;
		string triggerjson = trigger.ConvertToJSON();
		SysJobTrigger newtrigger = JSON.Deserialize<SysJobTrigger>(triggerjson);
		string detailjson = job.ConvertToJSON();
		SysJobDetail newdetail = JSON.Deserialize<SysJobDetail>(detailjson);
		newdetail.CreateType = JobCreateTypeEnum.Dynamic;
		newtrigger.JobId = newdetail.JobId;
		IScheduler scheduler = _schedulerFactory.GetJob(templateId.ToString());
		if (scheduler == null)
		{
			_schedulerFactory.AddJob(job, trigger);
			return;
		}
		TriggerBuilder triggerbuilder = scheduler.GetTriggerBuilder(templateId.ToString());
		if (triggerbuilder == null)
		{
			scheduler.UpdateTrigger(trigger);
		}
		else
		{
			scheduler.UpdateTrigger(trigger);
		}
	}

	/// <summary>
	/// 根据账单模板id，获取账单模板内容
	/// </summary>
	/// <param name="id"></param>
	/// <returns></returns>
	[HttpGet("/bill/detail")]
	public async Task<dynamic> GetBillTemplateDetail([FromQuery] long id)
	{
		BillTemplate data = await (from w in _rep.Context.Queryable<BillTemplate>()
			where w.Id == id
			select w).FirstAsync();
		if (data == null)
		{
			throw Oops.Bah("账单不存在或已被删除");
		}
		List<long> billtypes = data.BillTypes;
		List<BillType> types = await (from w in _rep.AsQueryable()
			where billtypes.Contains(w.Id)
			select w).ToListAsync();
		return new BillTemplateOutput
		{
			CallRules = data.CallRules,
			LimitAmount = data.LimitAmount,
			Name = data.Name,
			ExpiryDate = data.ExpiryDate,
			BillTypes = types.Select((BillType s) => new BillTypesOutput
			{
				Id = s.Id,
				Name = s.Name
			}).ToList()
		};
	}

	/// <summary>
	/// 删除房间账单
	/// </summary>
	/// <param name="id"></param>
	/// <returns></returns>
	[HttpPost("/bill/removeroombill")]
	public async Task RemoveRoomBill([FromQuery] long id)
	{
		CustomRoomBill bill = await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where w.Id == id
			select w).FirstAsync();
		if (bill == null)
		{
			throw Oops.Bah("该房间账单不存在或已被删除，请刷新后重试");
		}
		if (bill.Status == 2)
		{
			throw Oops.Bah("该房间账单已结束，无法删除");
		}
		await _rep.Context.Deleteable(bill).ExecuteCommandAsync();
	}

	/// <summary>
	/// 关闭房间账单
	/// </summary>
	/// <param name="id"></param>
	/// <returns></returns>
	[HttpPost("/bill/closeroombill")]
	public async Task CloseRoomBill([FromQuery] long id)
	{
		CustomRoomBill bill = await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where w.Id == id
			select w).FirstAsync();
		if (bill == null)
		{
			throw Oops.Bah("该房间账单不存在或已被删除，请刷新后重试");
		}
		if (bill.Status != 1)
		{
			throw Oops.Bah("该房间账单状态已发生改变，请刷新后重试");
		}
		bill.Status = 2;
		await _rep.Context.Updateable(bill).UpdateColumns((CustomRoomBill it) => new { it.Status }).ExecuteCommandAsync();
	}

	/// <summary>
	/// 获取业主账单
	/// </summary>
	/// <param name="page"></param>
	/// <param name="limit"></param>
	/// <param name="query"></param>
	/// <param name="roomId"></param>
	/// <param name="status"></param>
	/// <returns></returns>
	[HttpGet("/bill/getownerbill")]
	public async Task<dynamic> GetOwnerBill(int page, int limit, string query, long? roomId, int? status)
	{
		if (status.GetValueOrDefault() != 1 && status.GetValueOrDefault() != 2)
		{
			throw Oops.Bah("非法请求");
		}
		ISugarQueryable<CustomRoomBill> queryable = from u in _rep.Context.Queryable<CustomRoomBill>()
			where u.OwnerId == (long?)_userManager.UserId
			where (int?)u.Status == status
			select u;
		if (!string.IsNullOrWhiteSpace(query))
		{
			queryable = queryable.Where((CustomRoomBill u) => u.Name.Contains(query));
		}
		if (roomId.HasValue)
		{
			queryable = queryable.Where((CustomRoomBill u) => (long?)u.RoomId == roomId);
		}
		return await (from u in queryable
			orderby u.CreateTime descending
			select new CustomRoomBillOutput
			{
				Name = u.Name,
				Id = u.Id,
				RoomId = u.RoomId,
				Bills = u.Bills,
				ExpiryDate = from w2 in SqlFunc.Subqueryable<BillTemplate>()
					where w2.Id == u.BillTemplateId
					select w2.ExpiryDate,
				Status = u.Status,
				Remark = u.Remark,
				CallRules = from w2 in SqlFunc.Subqueryable<BillTemplate>()
					where w2.Id == u.BillTemplateId
					select w2.CallRules,
				RoomName = from w1 in SqlFunc.Subqueryable<Room>()
					where w1.Id == u.RoomId
					select w1.Name
			}).ToPagedListAsync(page, limit);
	}

	/// <summary>
	/// 获取详细内容
	/// </summary>
	/// <param name="id"></param>
	/// <returns></returns>
	[HttpGet("/bill/getownerbilldetail")]
	public async Task<dynamic> GetOwnerBillDetail([FromQuery] long id)
	{
		List<CustomBill> billtypes = (await (from w in _rep.Context.Queryable<CustomRoomBill>()
			where w.Id == id
			select w).FirstAsync())?.Bills;
		if (billtypes == null || billtypes.Count == 0)
		{
			throw Oops.Bah("无效账单");
		}
		List<long> types = billtypes.Select((CustomBill s) => s.BillType).ToList();
		List<BillType> typeNames = await (from w in _rep.AsQueryable()
			where types.Contains(w.Id)
			select w).ToListAsync();
		return billtypes.Select((CustomBill s) => new
		{
			name = (from w in typeNames
				where w.Id == s.BillType
				select w into s1
				select s1.Name).FirstOrDefault(),
			billType = s.BillType,
			fee = s.Fee
		}).ToList();
	}
}
